1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.base;
18  
19  import com.google.common.annotations.GwtCompatible;
20  
21  import junit.framework.TestCase;
22  
23  import java.util.Iterator;
24  import java.util.NoSuchElementException;
25  
26  /**
27   * Unit test for {@code AbstractIterator}.
28   *
29   * @author Kevin Bourrillion
30   */
31  @GwtCompatible(emulated = true)
32  // TODO(cpovirk): why is this slow (>1m/test) under GWT when fully optimized?
33  public class AbstractIteratorTest extends TestCase {
34  
35    public void testDefaultBehaviorOfNextAndHasNext() {
36  
37      // This sample AbstractIterator returns 0 on the first call, 1 on the
38      // second, then signals that it's reached the end of the data
39      Iterator<Integer> iter = new AbstractIterator<Integer>() {
40        private int rep;
41        @Override public Integer computeNext() {
42          switch (rep++) {
43            case 0:
44              return 0;
45            case 1:
46              return 1;
47            case 2:
48              return endOfData();
49            default:
50              fail("Should not have been invoked again");
51              return null;
52          }
53        }
54      };
55  
56      assertTrue(iter.hasNext());
57      assertEquals(0, (int) iter.next());
58  
59      // verify idempotence of hasNext()
60      assertTrue(iter.hasNext());
61      assertTrue(iter.hasNext());
62      assertTrue(iter.hasNext());
63      assertEquals(1, (int) iter.next());
64  
65      assertFalse(iter.hasNext());
66  
67      // Make sure computeNext() doesn't get invoked again
68      assertFalse(iter.hasNext());
69  
70      try {
71        iter.next();
72        fail("no exception thrown");
73      } catch (NoSuchElementException expected) {
74      }
75    }
76  
77    public void testSneakyThrow() throws Exception {
78      Iterator<Integer> iter = new AbstractIterator<Integer>() {
79        boolean haveBeenCalled;
80        @Override public Integer computeNext() {
81          if (haveBeenCalled) {
82            fail("Should not have been called again");
83          } else {
84            haveBeenCalled = true;
85            sneakyThrow(new SomeCheckedException());
86          }
87          return null; // never reached
88        }
89      };
90  
91      // The first time, the sneakily-thrown exception comes out
92      try {
93        iter.hasNext();
94        fail("No exception thrown");
95      } catch (Exception e) {
96        if (!(e instanceof SomeCheckedException)) {
97          throw e;
98        }
99      }
100 
101     // But the second time, AbstractIterator itself throws an ISE
102     try {
103       iter.hasNext();
104       fail("No exception thrown");
105     } catch (IllegalStateException expected) {
106     }
107   }
108 
109   public void testException() {
110     final SomeUncheckedException exception = new SomeUncheckedException();
111     Iterator<Integer> iter = new AbstractIterator<Integer>() {
112       @Override public Integer computeNext() {
113         throw exception;
114       }
115     };
116 
117     // It should pass through untouched
118     try {
119       iter.hasNext();
120       fail("No exception thrown");
121     } catch (SomeUncheckedException e) {
122       assertSame(exception, e);
123     }
124   }
125 
126   public void testExceptionAfterEndOfData() {
127     Iterator<Integer> iter = new AbstractIterator<Integer>() {
128       @Override public Integer computeNext() {
129         endOfData();
130         throw new SomeUncheckedException();
131       }
132     };
133     try {
134       iter.hasNext();
135       fail("No exception thrown");
136     } catch (SomeUncheckedException expected) {
137     }
138   }
139 
140   public void testCantRemove() {
141     Iterator<Integer> iter = new AbstractIterator<Integer>() {
142       boolean haveBeenCalled;
143       @Override public Integer computeNext() {
144         if (haveBeenCalled) {
145           endOfData();
146         }
147         haveBeenCalled = true;
148         return 0;
149       }
150     };
151 
152     assertEquals(0, (int) iter.next());
153 
154     try {
155       iter.remove();
156       fail("No exception thrown");
157     } catch (UnsupportedOperationException expected) {
158     }
159   }
160 
161   public void testReentrantHasNext() {
162     Iterator<Integer> iter = new AbstractIterator<Integer>() {
163       @Override protected Integer computeNext() {
164         hasNext();
165         return null;
166       }
167     };
168     try {
169       iter.hasNext();
170       fail();
171     } catch (IllegalStateException expected) {
172     }
173   }
174 
175   // Technically we should test other reentrant scenarios (4 combinations of
176   // hasNext/next), but we'll cop out for now, knowing that 
177   // next() both start by invoking hasNext() anyway.
178 
179   /**
180    * Throws a undeclared checked exception.
181    */
182   private static void sneakyThrow(Throwable t) {
183     class SneakyThrower<T extends Throwable> {
184       @SuppressWarnings("unchecked") // intentionally unsafe for test
185       void throwIt(Throwable t) throws T {
186         throw (T) t;
187       }
188     }
189     new SneakyThrower<Error>().throwIt(t);
190   }
191 
192   private static class SomeCheckedException extends Exception {
193   }
194 
195   private static class SomeUncheckedException extends RuntimeException {
196   }
197 }
198